/***************************************************************************
 *   Copyright (C) 2004 by cyril dupuit                                    *
 *   cyrildupuit@hotmail.com                                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

//*****************************************************************************
// Nom du module : ProcInfo.c
// Description :  Obtention d'infos sur les processus.
//*****************************************************************************
 
// Historique :
// 28/10/04 : Creation du module V1.0  

#include "Config.h"
#include "Kernel/Parental.h"
#include "Arch/Arch.h"
#include "Kernel/Core.h"
#include "Kernel/Message.h"
#include "Kernel/Semaphor.h"
#include "Kernel/Delay.h"
#include "Kernel/Schedule.h"
#include "Kernel/DynTimer.h"
#include "Kernel/Event.h"
#include "Arch/Pentium/Kernel/malloc.h"
#include "Kernel/Errors.h"
#include "Kernel/Thread.h"
#include "Misc/Console.h"
#include "Kernel/Process.h"
#include "Lib/StdLib/stdio.h"

#ifdef KOALYS_DEBUGGER
#include "Misc/Debug.h"
#include "Arch/Pentium/Kernel/Serial.h"
#endif

//*****************************************************************************

static const char SignI[] = {0xB3, ' ', ' ', ' ', '\0'};// |
static const char SignF[] = {0xC3, 0xC4, 0xC4, 0xC4,'\0'};// |-
static const char SignZ[] = {0xC0, 0xC4, 0xC4, 0xC4,'\0'};// '-
static const char SignSpace[] = "    ";
static const char SignNone[] = "";

//*****************************************************************************

static void DrawVerticalLines(ParentalLinkTree_t * pTree, LinkListID_t StackID);
static void TaskTreeDeleteObjects(LinkListID_t ProcessListID, LinkListID_t StackID);

//*****************************************************************************
// But de la fonction : Afficher la liste des processus
// Entree : Aucune
// Parametre retourne : ERROR si memoire disponible insuffisante, TRUE sinon
//*****************************************************************************
Status_t ProcessListShow(void)
{
	char Tab[100];
	unsigned int Line = 0;
	Count_t ReadyThread;
	Ticks_t TimeUsed, TotalTime;
	Priority_t Priority;
	unsigned int Count;
	QueueID_t QProcessList;
	ProcessID_t ProcessID;
	
	Count = ProcessCount();
	QProcessList = QueueCreate(Count, QUEUE_FIFO);
	if(QProcessList == 0)
	{//Creation de la file impossible
		SetLastError(INSUFFICIENT_MEMORY_AVAILABLE);
		return ERROR;
	}
	
	printf("Liste des processus (%d) : \n", Count);
	
	if(ProcessGetList(QProcessList) == FALSE)
	{//Une tache a ete cree entre temps, la file d'attente est trop petite
		printf("La liste ci-dessous est incomplete, un processus vient d'etre cree !\n");
	}
	
	ProcessID = (ProcessID_t)QueueRead(QProcessList);
	while(ProcessID != 0)
	{//Afficher la liste des processus
		if(ProcessGetName(ProcessID, Tab, sizeof(Tab)) == ERROR) return ERROR;
		printf("%ld\t%s", ProcessID, Tab);
		if(ProcessStatusString(ProcessID, Tab, sizeof(Tab)) == ERROR) return ERROR;
		if(ProcessGetReadyThread(ProcessID, &ReadyThread) == ERROR) return ERROR;
		printf(" : %s, %d threads pret.\n", Tab, ReadyThread);
		if(ProcessTimeUsed(ProcessID, &TimeUsed, &TotalTime) == ERROR) return ERROR;
		TimeUsed = (TimeUsed * 100) / TotalTime;
		printf("\t\tUtilisation : %ld%%\n", TimeUsed);
		if(ProcessGetExecProperty(ProcessID, &Priority, &TimeUsed) == ERROR) return ERROR;
		printf("\t\tPriorite %d, Tranche de temps %ld\n", Priority, TimeUsed);
		//Recuperer le prochain numero de processus
		ProcessID = (ProcessID_t)QueueRead(QProcessList);
		Line = (Line + 1) % 20;//20 equivaut au nombre de ligne sur la console
		if((Line == 0)&&(ProcessID != 0))
		{//Demander a l'utilisateur de presser une touche pour voir la suite de l'ecran
			if(Pause() != TRUE)
			{//L'utilisateur stoppe la visualisation
				QueueDelete(&QProcessList);
				
				return TRUE;
			}
		}
	}
	
	QueueDelete(&QProcessList);
	
	return TRUE;
}

//*****************************************************************************
// But de la fonction : Afficher des infos sur un processus
// Entree : Aucune
// Parametre retourne : ERROR si memoire disponible insuffisante, TRUE sinon
//*****************************************************************************
Status_t ProcessShow(ProcessID_t ProcessID)
{
	Process_t * pProcess = (Process_t *)ProcessID;
	ThreadCount_t ThreadCout;
	QueueID_t QueueID;
	ThreadID_t ThreadID;
	unsigned int Line = 0;
	Priority_t Priority;
	unsigned long TaskSwitchs, KernelSwitchs;
	Coord_t X, Y;
	char Tab[50];
	Status_t Status;
	
	Status = ProcessIDVerify(ProcessID);
	
	if(Status == FALSE)
	{//Le numero d'identification est incorrect
		SetLastError(INCORRECT_PROCESS_ID);
		return ERROR;
	}
	else if(Status == ERROR)
	{//La memoire est insuffisante
		SetLastError(INSUFFICIENT_MEMORY_AVAILABLE);
		return ERROR;
	}
	
	if(ProcessGetThreadCount(ProcessID, &ThreadCout) == ERROR)
	{//Le numero d'identification du processus est incorrect
		SetLastError(INCORRECT_PROCESS_ID);
		return ERROR;
	}
	
	QueueID = QueueCreate(ThreadCout, QUEUE_FIFO);
	if(QueueID == 0)
	{//La memoire disponible est insuffisante
		SetLastError(INSUFFICIENT_MEMORY_AVAILABLE);
		return ERROR;
	}
	
	printf("Liste des threads composant le processus : %ld : ", pProcess->ProcessID);
	if(pProcess->pConsole != NULL)
	{
		printf("%s\n", pProcess->pConsole->pName);
	}
	else printf("NoName\n");
	
	
	if(ProcessGetThreadList(ProcessID, QueueID) == FALSE)
	{//La file d'attente est trop petite
		printf("La liste ci-dessous est incomplete\n");
	}
	
	if(ConsoleGetCursorPosition(&X, &Y) == ERROR) return ERROR;
	if(printf("ID :") == -1) return ERROR;
	GotoXY(X + 10, Y);
	if(printf("Etat :") == -1) return ERROR;
	GotoXY(X + 40, Y);
	if(printf("Priorite :") == -1) return ERROR;
	GotoXY(X + 52, Y);
	if(printf("Commutations :\n") == -1) return ERROR;
	
	ThreadID = (ThreadID_t)QueueRead(QueueID);
	while(ThreadID != 0)
	{
		if(ConsoleGetCursorPosition(&X, &Y) == ERROR) return ERROR;

		printf("%ld", ThreadID);
		//Afficher l'etat de la tache
		GotoXY(X + 10, Y);
		if(ThreadStatusString(ThreadID, Tab, sizeof(Tab)) == ERROR) return ERROR;
		if(ThreadStatus(ThreadID, &Status) == ERROR) return ERROR;
		if(printf("%s ou 0x%x", Tab, Status) == -1) return ERROR;
		//Afficher la priorite de cette tache
		GotoXY(X + 40, Y);
		if(ThreadPriorityGet(ThreadID, &Priority) == ERROR) return ERROR;
		if(printf("%ld", (unsigned long)Priority) == -1) return ERROR;
		//Affichage du taux de commutation
		GotoXY(X + 52, Y);
		if(ThreadSwitchCount(ThreadID, &TaskSwitchs, &KernelSwitchs) == ERROR) return ERROR;
		if(printf("%ld/%ld\n", TaskSwitchs, KernelSwitchs) == -1) return ERROR;
		
		//Lire le prochain thread
		ThreadID = (ThreadID_t)QueueRead(QueueID);
		
		Line = (Line + 1) % 20;//20 equivaut au nombre de ligne sur la console
		if((Line == 0)&&(ThreadID != 0))
		{//Demander a l'utilisateur de presser une touche pour voir la suite de l'ecran
			if(Pause() != TRUE)
			{//L'utilisateur stoppe la visualisation
				QueueDelete(&QueueID);
				
				return TRUE;
			}
		}
	}
	
	QueueDelete(&QueueID);
	
	return TRUE;
}

//*****************************************************************************
// But de la fonction : Afficher l'arbre des processus
// Entree : Aucune
// Parametre retourne : ERROR si memoire disponible insuffisante, TRUE sinon
//*****************************************************************************
Status_t ProcessTreeShow(void)
{
	LinkListID_t ProcessList;
	LNode_t * pPosition;
	ParentalLinkTree_t * pTree;
	unsigned int Line = 0;
	char * pSign;
	Count_t TreeCount;
	Status_t PrevStatus = 0;
	LinkListID_t StackID;
	
	ProcessList = ParentalLinkGetTree();
	if(ProcessList == 0)
	{//Memoire insuffisante
		SetLastError(INSUFFICIENT_MEMORY_AVAILABLE);
		return ERROR;
	}

	StackID = LinkListCreate();
	if(StackID == 0)
	{//Memoire insuffisante
		TaskTreeDeleteObjects(ProcessList, StackID);
		SetLastError(INSUFFICIENT_MEMORY_AVAILABLE);
		return ERROR;
	}

	printf("Arbre genealogique des processus :\n");
	
	//Connaitre le nombre d'element dans la liste
	TreeCount = LinkListGetCount(ProcessList);

	//Recuperer le noeud de tete
	pPosition = LinkListGetHeadPosition(ProcessList);
	pTree = (ParentalLinkTree_t *)LinkListGetNext(ProcessList, &pPosition);
	while(pTree != NULL)
	{//Explorer la liste
		DrawVerticalLines(pTree, StackID);//Tracer les lignes verticales
		//Memoriser le niveau lors d'une double connexion (Fille et Soeur)
		if((IsSet(pTree->Status, BROTHER_PRESENT))&&(IsSet(pTree->Status, CHILD_PRESENT)))
		{//Si soeur et fille presente, memoriser le niveau
			if(LinkListAddTail(StackID, (void *)pTree->Level) == NULL)
			{//Memoire insuffisante
				TaskTreeDeleteObjects(ProcessList, StackID);
				SetLastError(INSUFFICIENT_MEMORY_AVAILABLE);
				return ERROR;
			}
		}
		//Machine d'etat du trace des traits
		if(pTree->Level == 0)
		{//Ne rien faire
			pSign = (char *)SignNone;
		}
		else if((!IsSet(PrevStatus, BROTHER_PRESENT))&&(IsSet(pTree->Status, BROTHER_PRESENT)))
		{//Noeud precedent n'a pas de soeur, le courant en a une
			pSign = (char *)SignF;
		}
		else if(!IsSet(pTree->Status, BROTHER_PRESENT))
		{//Noeud precedent a une soeur, pas le courant
			pSign = (char *)SignZ;
		}
		else if(!IsSet(PrevStatus, BROTHER_PRESENT))
		{
			pSign = (char *)SignZ;
		}
		else
		{
			pSign = (char *)SignF;
		}
		PrevStatus = pTree->Status;
		//Afficher la ou les branches et le nom
		printf("%s%s\n", pSign, pTree->pName);
		//Changer d'element	
		pTree = (ParentalLinkTree_t *)LinkListGetNext(ProcessList, &pPosition);
		
		Line = (Line + 1) % 20;//20 equivaut au nombre de ligne sur la console
		if((Line == 0)&&(pTree != NULL))
		{//Demander a l'utilisateur de presser une touche pour voir la suite de l'ecran
			if(Pause() != TRUE)
			{//L'utilisateur stoppe la visualisation
				while(pTree != NULL)
				{//Explorer la liste pour supprimer tout les noeuds
					TaskTreeDeleteObjects(ProcessList, StackID);
					
					return TRUE;
				}
			}
		}
	}

	TaskTreeDeleteObjects(ProcessList, StackID);
	
	return TRUE;
}


//*****************************************************************************
// But de la procedure : Tracer les traits verticaux
// Entrees :
//	pTree : Pointeur sur le noeud a visualiser
//	StackID : Numero d'identification de la "pile"
// Sortie : Aucune
//*****************************************************************************
static void DrawVerticalLines(ParentalLinkTree_t * pTree, LinkListID_t StackID)
{
	Status_t Status;
	unsigned int I, OldLevel, Level;
	LNode_t * pStackPos;
	LNode_t * pOldStackPos;
	char * pSign;
	
	for(I = 1; I < pTree->Level; I++)
	{//
		OldLevel = 0;
		Status = FALSE;
		pStackPos = LinkListGetHeadPosition(StackID);
		while(pStackPos != NULL)
		{//Explorer la liste des niveaux
			pOldStackPos = pStackPos;
			Level = (int)LinkListGetNext(StackID, &pStackPos);
			if(Level == OldLevel)
			{//Niveau identique, supprimer le noeud courant
				LinkListRemoveAt(StackID, pOldStackPos);	
			}	
			if(I == Level)
			{//Tracer un trait vertical
				Status = TRUE;
				break;
			}
			OldLevel = Level;
		}
		if(Status == TRUE)
		{//Tracer un trait vertical
			pSign = (char *)SignI;
		}
		else pSign = (char *)SignSpace;//Ne pas tracer de trait
	
		printf(pSign);
	}
	if(!IsSet(pTree->Status, (BROTHER_PRESENT + CHILD_PRESENT)))
	{//Supprimer le niveau si le noeud n'a ni soeur ni enfant
		LinkListRemoveTail(StackID);
	}
}

//*****************************************************************************
// But de la procedure : Detruire les objets utilise pour la construction de
// l'arbre
// Entrees :
//	ProcessListID : Numero d'identification de la liste des taches
//	StackID : Numero d'identification de la "pile"
// Sortie : Aucune
//*****************************************************************************
static void TaskTreeDeleteObjects(LinkListID_t ProcessListID, LinkListID_t StackID)
{
	LNode_t * pPosition;
	ParentalLinkTree_t * pTree;

	pPosition = LinkListGetHeadPosition(ProcessListID);
	pTree = (ParentalLinkTree_t *)LinkListGetNext(ProcessListID, &pPosition);
	while(pTree != NULL)
	{//Explorer la liste pour supprimer tout les noeuds
		kfree(pTree->pName);
		kfree(pTree);
		pTree = (ParentalLinkTree_t *)LinkListGetNext(ProcessListID, &pPosition);
	}
	LinkListDelete(&ProcessListID);
	LinkListDelete(&StackID);
}

//*****************************************************************************
